package com.whfc.hdc.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.whfc.common.exception.BizException;
import com.whfc.common.result.PageData;
import com.whfc.common.result.ResultEnum;
import com.whfc.common.util.PageUtil;
import com.whfc.common.util.RandomUtil;
import com.whfc.hdc.dao.AppDeviceCmdMapper;
import com.whfc.hdc.dao.AppDeviceFeatureMapper;
import com.whfc.hdc.dao.AppDeviceMapper;
import com.whfc.hdc.dao.AppDeviceParamMapper;
import com.whfc.hdc.dto.AppDeviceCmdDTO;
import com.whfc.hdc.dto.AppDeviceDTO;
import com.whfc.hdc.dto.AppDeviceFeatureDTO;
import com.whfc.hdc.dto.AppDeviceParamDTO;
import com.whfc.hdc.entity.AppDevice;
import com.whfc.hdc.entity.AppDeviceCmd;
import com.whfc.hdc.entity.AppDeviceFeature;
import com.whfc.hdc.enums.DeviceCmdState;
import com.whfc.hdc.redis.DeviceRedisDao;
import com.whfc.hdc.service.AppDeviceService;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;
import java.util.List;

/**
 * @Description:
 * @author: xugcheng
 * @version: 1.0
 * @date: 2021-09-28 10:08
 */
@DubboService(interfaceClass = AppDeviceService.class, version = "1.0.0")
public class AppDeviceServiceImpl implements AppDeviceService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 指令过期时间(30s)
     */
    private static final long CMD_EXPIRE_TIMES = 5 * 1000;

    @Autowired
    private AppDeviceMapper deviceMapper;

    @Autowired
    private AppDeviceParamMapper deviceParamMapper;

    @Autowired
    private DeviceRedisDao deviceRedisDao;

    @Autowired
    private AppDeviceCmdMapper appDeviceCmdMapper;

    @Autowired
    private AppDeviceFeatureMapper featureMapper;

    @Override
    public AppDeviceDTO getDeviceById(Integer id) {
        AppDeviceDTO deviceDTO = deviceRedisDao.getById(id);
        if (deviceDTO == null) {
            AppDevice device = deviceMapper.selectByPrimaryKey(id);
            if (device != null) {
                deviceDTO = this.to(device);
                deviceRedisDao.set(deviceDTO);
            }
        }
        return deviceDTO;
    }

    @Override
    public AppDeviceDTO getDeviceBySn(String sn) {
        AppDeviceDTO deviceDTO = deviceRedisDao.getBySn(sn);
        if (deviceDTO == null) {
            AppDevice device = deviceMapper.selectBySn(sn);
            if (device != null) {
                deviceDTO = this.to(device);
                deviceRedisDao.set(deviceDTO);
            }
        }
        return deviceDTO;
    }

    @Override
    public AppDeviceDTO getDeviceBySnAndType(String sn, Integer deviceType) {
        return deviceMapper.getDeviceBySnAndType(sn,deviceType);
    }

    @Override
    public AppDeviceDTO getDeviceByCode(String code) {
        AppDeviceDTO deviceDTO = deviceRedisDao.getByCode(code);
        if (deviceDTO == null) {
            AppDevice device = deviceMapper.selectByCode(code);
            if (device != null) {
                deviceDTO = this.to(device);
                deviceRedisDao.set(deviceDTO);
            }
        }

        return deviceDTO;
    }

    @Override
    public PageData<AppDeviceDTO> getDeviceList(Integer pageNum, Integer pageSize, Integer type, String sn) {

        PageHelper.startPage(pageNum, pageSize);
        List<AppDeviceDTO> list = deviceMapper.selectByTypeAndSn(type, sn);
        PageHelper.clearPage();

        return PageUtil.pageData(PageInfo.of(list));
    }

    @Override
    public void cleanDeviceCache(Integer id) {
        AppDevice device = deviceMapper.selectByPrimaryKey(id);
        if (device != null) {
            AppDeviceDTO deviceDTO = this.to(device);
            deviceRedisDao.del(deviceDTO);
        }
    }

    @Override
    public void generateDeviceSN(Integer deviceType, Integer startDeviceId, Integer endDeviceId) {

        for (int i = startDeviceId; i <= endDeviceId; i++) {
            String randomStr = RandomUtil.getRandomStr(4);
            String deviceTypeStr = this.leftPad(deviceType, 2, '0');
            String deviceIdStr = this.leftPad(i, 6, '0');
            StringBuffer str = new StringBuffer();
            str.append("FC");
            str.append(deviceTypeStr);
            str.append(deviceIdStr);
            str.append(randomStr);
            String sn = str.toString();
            logger.info("sn:{}", sn);
            AppDevice device = new AppDevice();
            device.setId(i);
            device.setType(deviceType);
            device.setSn(sn);
            device.setCode(sn);
            deviceMapper.insertSelective(device);
        }
    }

    @Override
    public List<AppDeviceParamDTO> getDeviceParam(Integer deviceId) {
        List<AppDeviceParamDTO> paramDTOList = deviceParamMapper.selectByDeviceId(deviceId);
        return paramDTOList;
    }

    @Override
    public void setDeviceParamRead(Integer deviceId) {
        deviceParamMapper.updateSendTimeAndState(deviceId, new Date());
    }

    @Override
    public void pubDeviceCmd(AppDeviceCmdDTO deviceCmdDTO) throws BizException {

        Integer deviceId = deviceCmdDTO.getDeviceId();
        AppDevice device = deviceMapper.selectByPrimaryKey(deviceCmdDTO.getDeviceId());
        if (device == null) {
            throw new BizException(ResultEnum.NOT_FOUND.getCode(), "该硬件不存在");
        }

        AppDeviceCmdDTO cmdDTO = appDeviceCmdMapper.selectLastCmdByDeviceId(deviceId);
        if (cmdDTO == null || isComplete(cmdDTO) || isExpired(cmdDTO)) {
            Integer deviceType = device.getType();
            AppDeviceCmd deviceCmd = new AppDeviceCmd();
            BeanUtils.copyProperties(deviceCmdDTO, deviceCmd);
            deviceCmd.setState(DeviceCmdState.NOT_SEND.getValue());
            deviceCmd.setPubTime(new Date());
            deviceCmd.setDeviceType(deviceType);
            appDeviceCmdMapper.insertSelective(deviceCmd);
        } else {
            logger.info("该硬件近期内已发送相同指令,请稍后尝试,deviceId:{},cmd:{}", deviceId, deviceCmdDTO.getCmd());
            throw new BizException(ResultEnum.PARAM_ERROR.getCode(), "该硬件近期内已发送相同指令,请稍后尝试");
        }
    }

    @Override
    public void setDeviceCmdSend(Integer deviceId, Integer cmd) throws BizException {
        AppDeviceCmdDTO cmdDTO = appDeviceCmdMapper.selectLastCmdByDeviceIdAndCmd(deviceId, cmd);
        if (cmdDTO != null) {
            int cnt = appDeviceCmdMapper.updateCmdSend(cmdDTO.getId(), new Date());
            logger.info("设置指令已发送,deviceId:{},cmd:{},state:{},ret:{}", deviceId, cmd, cmdDTO.getState(), cnt);
        }
    }

    @Override
    public void setDeviceCmdAck(Integer deviceId, Integer cmd) throws BizException {
        AppDeviceCmdDTO cmdDTO = appDeviceCmdMapper.selectLastCmdByDeviceIdAndCmd(deviceId, cmd);
        if (cmdDTO != null) {
            int cnt = appDeviceCmdMapper.updateCmdAck(cmdDTO.getId(), new Date());
            logger.info("设置指令已确认,deviceId:{},cmd:{},state:{},ret:{}", deviceId, cmd, cmdDTO.getState(), cnt);
        }
    }

    @Override
    public void setDeviceCmdSuccess(Integer deviceId, Integer cmd) throws BizException {
        AppDeviceCmdDTO cmdDTO = appDeviceCmdMapper.selectLastCmdByDeviceIdAndCmd(deviceId, cmd);
        if (cmdDTO != null) {
            int cnt = appDeviceCmdMapper.updateCmdSuccess(cmdDTO.getId(), new Date());
            logger.info("设置指令已成功,deviceId:{},cmd:{},state:{},ret:{}", deviceId, cmd, cmdDTO.getState(), cnt);
        }
    }

    @Override
    public void setDeviceCmdFail(Integer deviceId, Integer cmd) throws BizException {
        AppDeviceCmdDTO cmdDTO = appDeviceCmdMapper.selectLastCmdByDeviceIdAndCmd(deviceId, cmd);
        if (cmdDTO != null) {
            int cnt = appDeviceCmdMapper.updateCmdFail(cmdDTO.getId(), new Date());
            logger.info("设置指令已失效,deviceId:{},cmd:{},state:{},ret:{}", deviceId, cmd, cmdDTO.getState(), cnt);
        }
    }

    @Override
    public void addDeviceFeature(AppDeviceFeatureDTO featureDTO) throws BizException {
        logger.info("保存硬件特指值,deviceId:{},time:{}", featureDTO.getDeviceId(), featureDTO.getTime());
        AppDeviceFeature feature = new AppDeviceFeature();
        BeanUtils.copyProperties(featureDTO, feature);
        featureMapper.insertSelective(feature);
    }

    @Override
    public List<AppDeviceFeatureDTO> getDeviceFeatureS(Integer deviceId) throws BizException {
        return featureMapper.selectFeatureDTOListByDeviceId(deviceId);
    }

    /**
     * 指令过期
     *
     * @param cmdDTO
     * @return
     */
    private boolean isExpired(AppDeviceCmdDTO cmdDTO) {
        long diff = System.currentTimeMillis() - cmdDTO.getPubTime().getTime();
        return diff > CMD_EXPIRE_TIMES;
    }

    /**
     * 指令已完成
     *
     * @param cmdDTO
     * @return
     */
    private boolean isComplete(AppDeviceCmdDTO cmdDTO) {
        return DeviceCmdState.SUCCESS.getValue().equals(cmdDTO.getCmd())
                || DeviceCmdState.SUCCESS.getValue().equals(cmdDTO.getCmd());
    }

    /**
     * 生成SN
     *
     * @param deviceId
     * @param length
     * @param pad
     * @return
     */
    private String leftPad(int deviceId, int length, char pad) {
        StringBuffer str = new StringBuffer();
        String deviceIdStr = String.valueOf(deviceId);
        if (deviceIdStr.length() < length) {
            for (int i = 0; i < length - deviceIdStr.length(); i++) {
                str.append(pad);
            }
        }
        str.append(deviceIdStr);
        return str.toString();
    }

    /**
     * 对象转换
     *
     * @param device
     * @return
     */
    private AppDeviceDTO to(AppDevice device) {
        AppDeviceDTO deviceDTO = new AppDeviceDTO();
        BeanUtils.copyProperties(device, deviceDTO);
        return deviceDTO;
    }

    @Override
    public int rigisterDevice(Integer type, String sn, String code, String color) {
        AppDevice device = new AppDevice();
        device.setType(type);
        device.setSn(sn);
        device.setCode(code);
        device.setColor(color);
        return deviceMapper.insertSelective(device);
    }
}
